#include <mc/chunk.h>
#include <mc/blocks.h>
#include <util/tools.h>
#include <cstdint>
#include <iostream>

namespace mc
{

//! @brief 构造函数
Chunk::Chunk(const ChunkID& id) : 
    _id(id),
    _blockData(CHUNK_SZIE, std::vector<std::vector<BlockType>>(CHUNK_HEIGHT, std::vector<BlockType>(CHUNK_SZIE, BlockType::EMPTY)))
{}

//! @brief 构造函数
Chunk::Chunk(int x, int y) : Chunk(ChunkID(x, y))
{}

//! @brief 析构函数
Chunk::~Chunk() noexcept
{
#ifdef DEBUG
    std::cout << "free chunk\n";
#endif
    for (auto iter = this->_blocks.begin(); iter != this->_blocks.end(); ++iter)
        delete iter->second;
}

//! @brief 通过坐标计算区块 id
ChunkID Chunk::getChunkID(int64_t x, int64_t z)
{
    int idX, idZ;
    
    if (x < 0 && x % CHUNK_SZIE != 0)
        idX = x / CHUNK_SZIE - 1;
    else
        idX = x / CHUNK_SZIE;

    if (z < 0 && z % CHUNK_SZIE != 0)
        idZ = z / CHUNK_SZIE - 1;
    else 
        idZ = z / CHUNK_SZIE;

    return ChunkID(idX, idZ);
}

//! @brief 绘制区块
void Chunk::draw()
{
    for (auto iter = this->_blocks.begin(); iter != this->_blocks.end(); ++iter)
        iter->second->draw();
}

//! @brief 更新区块中方块的位置矩阵
void Chunk::updatePosition()
{
    for (auto iter = this->_blocks.begin(); iter != this->_blocks.end(); ++iter)
        iter->second->updatePositions();
}

//! @brief 放置方块
void Chunk::placeBlock(int64_t x, int64_t y, int64_t z, BlockType block, bool update)
{
    this->_blockData[x][y][z] = block;
    
    auto iter = this->_blocks.find(block);
    if (iter == this->_blocks.end())
    {
        Block* newBlk = BlockFactory::getBlock(block);
        this->_blocks[block] = newBlk;
        newBlk->place(this->_id.coord.x * CHUNK_SZIE + x, y, this->_id.coord.y * CHUNK_SZIE + z, update);
        return;
    }
    iter->second->place(this->_id.coord.x * CHUNK_SZIE + x, y, this->_id.coord.y * CHUNK_SZIE + z, update);
}

//! @brief 移除方块
BlockType Chunk::removeBlock(int64_t x, int64_t y, int64_t z, bool update)
{
    BlockType type = this->_blockData[x][y][z];

    this->_blocks[type]->remove(this->_id.coord.x * CHUNK_SZIE + x, y, this->_id.coord.y * CHUNK_SZIE + z, update);
    
    this->_blockData[x][y][z] = BlockType::EMPTY;
    return type;
}

}